#pragma once


#include "Common/MyFixes.h"

#include "Common/FileSystem/IFolder.h"
#include "Common/FileSystem/FileDir.h"
#include "Common/FileSystem/FileItem.h"

#include "Common/System/DLL.h"
#include "Common/System/Threading/SimpleWorkerPool.h"

#include "../../ViewSettings.h"


#include <assert.h>

#include <vector>
#include <memory>

#ifdef UNDER_CE
#define ROOT_FS_FOLDER _T("\\")
#else
#define ROOT_FS_FOLDER _T("C:\\\\")
#endif

namespace FileManager
{
namespace FileSystem
{
const int kParentIndex = -1;
static LPCWSTR kTempDirPrefix = _T("7zE");

//taken from App.cpp
// reduces path to part that exists on disk
void ReducePathToRealFileSystemPath(CString &path);
bool CheckFolderPath(const CString &path);
bool IsPathAbsolute(const CString &path);
int GetRealIconIndex(LPCWSTR path, DWORD attributes);
CString GetFolderPath(IFolderFolder* folder);
CString GetQuotedString(const CString &s);

//Most of the functions and variables are moved from CPanel class

struct CItemProperty
{
	CString Name;
	PROPID ID;	
	int Order;	
	UINT32 Width;
	VARTYPE Type;
	bool IsVisible;

	inline bool operator<(const CItemProperty &item) const
	{
		return (this->Order < item.Order);
	}

	inline bool operator==(const CItemProperty &item) const
	{
		return (this->Order == item.Order);
	}

};


class CItemProperties : public std::vector<CItemProperty>
{
public:
	int FindItemWithID(PROPID id)
	{
		for (size_t i = 0; i < size(); i++)
			if ((*this)[i].ID == id)
			{
				return i;
			}
		return -1;
	}
};


struct CTempFileInfo
{
	Common::FileSystem::File::CFileInfo FileInfo;
	CString ItemName;
	CString FolderPath;
	CString FilePath;	
	bool NeedDelete;

	CTempFileInfo() : NeedDelete(false) {}
	void DeleteDirAndFile() const
	{
		if (NeedDelete)
		{
			Common::FileSystem::DeleteFileAlways(FilePath);
			Common::FileSystem::RemoveDirectory(FolderPath);
		}
	}
	bool WasChanged(const Common::FileSystem::File::CFileInfo &newFileInfo) const
	{
		return newFileInfo.Size != FileInfo.Size ||
			CompareFileTime(&newFileInfo.ModificationTime, &FileInfo.ModificationTime) != 0;
	}
};

struct CFolderLink : public CTempFileInfo
{
	Common::System::DLL::CLibrary Library;
	CComPtr<IFolderFolder> ParentFolder;
	bool UsePassword;
	CString Password;
	bool IsVirtual;

	CString VirtualPath;
	CFolderLink() : UsePassword(false), IsVirtual(false) {}

	bool WasChanged(const Common::FileSystem::File::CFileInfo &newFileInfo) const
	{
		return IsVirtual || CTempFileInfo::WasChanged(newFileInfo);
	}

};


class CDirectory
{
	friend class CThreadFolderOperations;
public:

	CDirectory() :
		_flatMode(false),
		_flatModeForDisk(false),
		_flatModeForArc(false),
		_processNotify(true),
		_processTimer(true)

	{}

	~CDirectory()
	{
		CloseOpenFolders();
	}

	IFolderFolder* const GetIFolder() const
	{
		return _folder;
	}


	void DeleteItems(std::vector<UINT32> &indices, bool toRecycleBin);
	void DeleteItemsInternal(std::vector<UINT32> &indices);
	bool CreateFolder(CString &name);
	bool CreateFile(CString &name);

	CString GetItemName(int itemIndex) const;
	CString GetItemExtension(int itemIndex) const;
	CString GetItemPrefix(int itemIndex) const;
	CString GetItemRelPath(int itemIndex) const;
	CString GetItemFullPath(int itemIndex) const;
	bool IsItemFolder(int itemIndex) const;
	UINT64 GetItemSize(int itemIndex) const;


	void SetToRootFolder();
	HRESULT BindToPath(const CString &fullPath, const CString &arcFormat, bool &archiveIsOpened, bool &encrypted); // can be prefix
	HRESULT BindToPathAndRefresh(const CString &path);
	void OpenDrivesFolder();
	//Populates current directory string
	void LoadFullPath();
	void OpenParentFolder(CString &selectedItem);
	void CloseOpenFolders();
	//not used
	//void OpenRootFolder();

	void Release();
	HRESULT CreateShellContextMenu(const std::vector<UINT32> &operatedIndices,
								   CComPtr<IContextMenu> &systemContextMenu) const;
	void InvokeSystemCommand(const char* command, const std::vector<UINT32> &operatedIndices) const;
	CString GetFolderTypeID() const;
	bool IsFolderTypeEqTo(const wchar_t* s) const;
	bool IsRootFolder() const;
	bool IsFSFolder() const;
	bool IsFSDrivesFolder() const;
	bool IsArcFolder() const;
	bool IsFsOrDrivesFolder() const
	{
		return IsFSFolder() || IsFSDrivesFolder();
	}

	bool IsDeviceDrivesPrefix() const
	{
		return GetCurrentDirectory() == _T("\\\\.\\");
	}
	bool IsFsOrPureDrivesFolder() const
	{
		return IsFSFolder() || (IsFSDrivesFolder() && !IsDeviceDrivesPrefix());
	}

	CString GetFsPath() const;
	CString GetDriveOrNetworkPrefix() const;

	bool DoesItSupportOperations() const;

	//new functions
	bool EditName(int nIndex, const CString &newName);
	void SetWindow(HWND hWnd)
	{
		assert(nullptr != hWnd);
		m_hWnd = hWnd;
	}

	void SetThreadPool(Common::System::Threading::ThreadPoolPtr* pool)
	{
		assert(nullptr != pool);
		m_pool = *pool;
	}

	void SplitFiles(std::vector<UINT32> &indices);
	void CombineFiles(std::vector<UINT32> &indices);
	void SelectByType(int index, std::vector<bool> &selection, bool selectMode);
	void GetProperties(const std::vector<UINT32> &operatedIndices) const;

	const CString &GetCurrentDirectory() const
	{
		return _currentFolderPrefix;
	}

	const CString GetParentFolder() const
	{
		if(_parentFolders.empty())
		{
			return _currentFolderPrefix;
		}

		return GetFolderPath(_parentFolders[0]->ParentFolder);
	}

	bool IsArchive() const { return 0 < _parentFolders.size() ; }

	UINT32 GetNumberOfItems() const
	{
		UINT32 nCount;
		if (S_OK == _folder->GetNumberOfItems(&nCount))
		{
			return nCount;
		}
		return 0; //Not sure what to return
	}

	bool GetProcessTimer() { return _processTimer; }
	bool GetProcessNotify() { return _processNotify; }

	//Extracted from PanelItems.cpp
	//Returns false if COM query fails as per extracted logic
	bool FolderHasChanged() const ;
	
	//extracted from PanelItems.cpp function InitColumns
	void ResetProperties(CListViewInfo & listInfo);
	CItemProperties & GetAllProperties() {return _properties;}
	CItemProperties & GetVisibleProperties() { return _visibleProperties; }

protected:

	bool _processTimer;
	bool _processNotify;
	class CDisableTimerProcessing
	{
		bool _processTimerMem;
		bool _processNotifyMem;

		CDirectory &_directory;
	public:

		CDisableTimerProcessing(CDirectory &dir) : _directory(dir)
		{
			Disable();
		}
		void Disable()
		{
			_processTimerMem = _directory._processTimer;
			_processNotifyMem = _directory._processNotify;
			_directory._processTimer = false;
			_directory._processNotify = false;
		}
		void Restore()
		{
			_directory._processTimer = _processTimerMem;
			_directory._processNotify = _processNotifyMem;
		}
		~CDisableTimerProcessing()
		{
			Restore();
		}
		CDisableTimerProcessing &operator=(const CDisableTimerProcessing &)
		{
			;
		}
	};

public:
	void OpenFolderExternal(int index);
	void OpenFolder(int index);
	HRESULT OpenParentArchiveFolder();

	HRESULT OpenItemAsArchive(IInStream* inStream, const CTempFileInfo &tempFileInfo,
							  const CString &virtualFilePath, const CString &arcFormat, bool &encrypted);
	HRESULT OpenItemAsArchive(const CString &name, const CString &arcFormat, bool &encrypted);
	HRESULT OpenItemAsArchive(int index);
	void OpenItemInArchive(int index, bool tryInternal, bool tryExternal,
						   bool editMode);


	HRESULT OnOpenItemChanged(const CString &folderPath, const CString &itemName, bool usePassword, const CString &password);
	HRESULT OnOpenItemChanged(LPARAM lParam);

	void OpenItem(int index, bool tryInternal, bool tryExternal);
	bool EditItem(int index);

	bool ChangeComment(int nIndex);

	void ChangeFlatMode();
	bool GetFlatMode() const
	{
		return _flatMode;
	}
	void SetArcFlatMode(bool bMode)
	{
		_flatModeForArc = bMode;
	}
	bool GetArcFlatMode()
	{
		return _flatModeForArc;
	}

	void AddToArchive(std::vector<UINT32> &indices);
	bool ExtractArchives(std::vector<UINT32> &indices);
	bool TestArchives(std::vector<UINT32> &indices);

	void GetFilePaths(const std::vector<UINT32> &indices, CStringVector &paths) const;

	HRESULT CopyTo(const std::vector<UINT32> &indices, const CString &folder,
				   bool moveMode, bool showErrorMessages, CStringVector* messages,
				   bool &usePassword, CString &password);

	HRESULT CopyTo(const std::vector<UINT32> &indices, const CString &folder,
				   bool moveMode, bool showErrorMessages, CStringVector* messages)
	{
		bool usePassword = false;
		CString password;
		if (_parentFolders.size() > 0)
		{
			const auto &fl = _parentFolders.back();
			usePassword = fl->UsePassword;
			password = fl->Password;
		}
		return CopyTo(indices, folder, moveMode, showErrorMessages, messages, usePassword, password);
	}

	HRESULT CopyFrom(const CString &folderPrefix, const CStringVector &filePaths,
					 bool showErrorMessages, CStringVector* messages);

	CString GetItemsInfoString(const std::vector<UINT32> &indices);

protected:

	bool _flatMode;
	bool _flatModeForDisk;
	bool _flatModeForArc;

	CString _currentFolderPrefix;

	typedef std::vector<std::shared_ptr<CFolderLink> > ParentFolderTypeDef;

	ParentFolderTypeDef _parentFolders;
	Common::System::DLL::CLibrary _library;
	CComPtr<IFolderFolder> _folder;
	HWND m_hWnd;
	Common::System::Threading::ThreadPoolPtr m_pool;

	CItemProperties _properties;
	CItemProperties _visibleProperties;


};


} //namespace FileSystem
} //namespace FileManager